package com.qianfeng.smartdevice.websocket.handler;

import com.qianfeng.smartdevice.enums.DeviceStatus;
import com.qianfeng.smartdevice.event.DevicePowerEvent;
import com.qianfeng.smartdevice.event.DeviceStatusCheckEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import java.io.IOException;
import java.net.URI;
import java.util.concurrent.ConcurrentHashMap;


/**
 * Created by wenka on 2022/3/27 13:46
 *
 * @author Jackiechan
 * @version 1.0
 * @since 1.0
 */
@Component
public class BaseCommandHandler extends TextWebSocketHandler {
    private ApplicationContext context;

    public ApplicationContext getContext() {
        return context;
    }

    @Autowired
    public void setContext(ApplicationContext context) {
        this.context = context;
    }

    /**
     * 保存所有连接设备的map
     */
    private ConcurrentHashMap<String, WebSocketSession> allClients = new ConcurrentHashMap<>();
    /**
     * 记录每个设备最后一次心跳的时间
     */
    private ConcurrentHashMap<String, Long> lastHeartBeatMap = new ConcurrentHashMap<>();

    protected ConcurrentHashMap<String, WebSocketSession> getAllClients() {
        return allClients;
    }

    protected ConcurrentHashMap<String, Long> getLastHeartBeatMap() {
        return lastHeartBeatMap;
    }

    /**
     * 连接建立
     *
     * @param session
     * @throws Exception
     */
    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        //获取到当前连接的标识
        URI uri = session.getUri();
        //获取到字符串格式的地址,地址的最后一位是标识
        String uriString = uri.toString();
        String deviceKye = uriString.substring(uriString.lastIndexOf("/") + 1);
        session.getAttributes().put("deviceKey", deviceKye);
        //检查下当前标识有没有之前连接过
        WebSocketSession webSocketSession = allClients.get(deviceKye);
        if (webSocketSession != null) {
            //之前链接过，关闭之前的连接，替换为新的连接
            webSocketSession.close();
        }
        //保存会话
        allClients.put(deviceKye, session);
        //记录最后的时间
        lastHeartBeatMap.put(deviceKye, System.currentTimeMillis());
        //设备连接之后，在这里更心状态
        //我们要保证单一职责，更新设备状态的操作和我们之前websocket没关系，我们的websocket主要负责记录设备，主动给设备推送数据
        //交给专门更新的人处理，可以通过时间发送出去，谁接收谁处理，谁处理谁接收
        //我们发送时间的目的是更新某个设备为在线状态，并且记录连接的ip和区域，我们需要这些数据，其中设备的编号，IP正能在这边获取
        context.publishEvent(new DevicePowerEvent(deviceKye, session.getRemoteAddress().getHostString(), DeviceStatus.ONLINE));
    }

    /**
     * 收到消息
     *
     * @param session
     * @param message
     * @throws Exception
     */
    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        System.err.println("收到消息");
        //记录当前设备最后一次给我们发送数据的时间，当前设备，第二个是最后一次时间，典型的key-value
        Object deviceKey = session.getAttributes().get("deviceKey");
        //记录最后的时间，用于心跳检查用
        lastHeartBeatMap.put((String) deviceKey, System.currentTimeMillis());
    }

    /**
     * 连接关闭
     *
     * @param session
     * @param status
     * @throws Exception
     */
    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        System.err.println("有设备离线了");
        Object deviceKey = session.getAttributes().get("deviceKey");
        allClients.remove(deviceKey);
        context.publishEvent(new DevicePowerEvent((String) deviceKey, null, DeviceStatus.OFFLINE));


    }

    @EventListener
    public void onEvent(DeviceStatusCheckEvent event) {
        System.out.println("设备心跳检查开始了");
        allClients.entrySet().parallelStream().forEach(stringWebSocketSessionEntry -> {
            //当前遍历的设备的会话
            WebSocketSession socketSession = stringWebSocketSessionEntry.getValue();
            String deviceId = stringWebSocketSessionEntry.getKey();
            Long lastHeartbeatTime = lastHeartBeatMap.get(deviceId);
            if (System.currentTimeMillis() - lastHeartbeatTime > 30000) {
                //当前时间减去最后一次心跳时间大于30秒，我们就认为你断线了
                //短线做三个事情，一个是关闭session，一个是从allClients移除设备，一个是从心跳的map中移除设备
                //遍历的时候能直接删除数据吗？
                if (socketSession.isOpen()) {
                    //关闭，触发上面的关闭监听，从map中移除数据
                    try {
                        socketSession.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                lastHeartBeatMap.remove(deviceId);
            }
        });

    }

}
